﻿using Castle.DynamicProxy;
using FreeSql;
using ManaSky.Common.Model;
using ManaSky.DBCore.Attributes;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ManaSky.DBCore.Interceptor
{
    /// <summary>
    /// ** 描述：拦截器和Autofac来配合使用
    /// ** 时间：2022-7-03
    /// ** 作者：杨逢春
    /// </summary>
    public class OperateInterceptor : IInterceptor
    {
        private OperateAsyncInterceptor _asyncInterceptor { get; }
        public OperateInterceptor(OperateAsyncInterceptor interceptor)
        {
            _asyncInterceptor = interceptor;
        }
        public void Intercept(IInvocation invocation)
        {
            try
            {
                _asyncInterceptor.ToInterceptor().Intercept(invocation);
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }

    public class OperateAsyncInterceptor : IAsyncInterceptor
    {
        private readonly UnitOfWorkManager _unitOfWorkManager;
        private readonly ILogger<OperateAsyncInterceptor> _logger;
        public OperateAsyncInterceptor(UnitOfWorkManager unitOfWorkManager, ILogger<OperateAsyncInterceptor> logger)
        {
            _unitOfWorkManager = unitOfWorkManager;
            _logger = logger;
        }

        /// <summary>
        /// 拦截同步执行的方法
        /// </summary>
        /// <param name="invocation"></param>
        public void InterceptSynchronous(IInvocation invocation)
        {
            if (GetTransactionAttribute(invocation) != null)
            {
                using (var _unitOfWork = BeginTransaction(invocation))
                {
                    try
                    {
                        invocation.Proceed();
                        if (invocation.ReturnValue is OperateResult)
                        {
                            var result = invocation.ReturnValue as OperateResult;
                            if (result != null && result.Success)
                                _unitOfWork?.Commit();
                            else
                                _unitOfWork?.Rollback();
                        }
                        else
                            _unitOfWork?.Commit();
                    }
                    catch (Exception)
                    {
                        _unitOfWork?.Rollback();
                        throw;
                    }
                }
            }
            else
                invocation.Proceed();
        }

        /// <summary>
        /// 拦截返回结果为Task的方法
        /// </summary>
        /// <param name="invocation"></param>
        public void InterceptAsynchronous(IInvocation invocation)
        {
            invocation.ReturnValue = ExtensionInterceptAsynchronous(invocation);
        }

        /// <summary>
        /// 拦截返回结果为Task<TResult>的方法
        /// </summary>
        /// <param name="invocation"></param>
        /// <typeparam name="TResult"></typeparam>
        public void InterceptAsynchronous<TResult>(IInvocation invocation)
        {
            invocation.ReturnValue = ExtensionInterceptAsynchronous<TResult>(invocation);
        }

        #region 方法

        /// <summary>
        /// 拦截返回结果为Task的方法扩展
        /// </summary>
        /// <param name="invocation"></param>
        public async Task ExtensionInterceptAsynchronous(IInvocation invocation)
        {
            if (GetTransactionAttribute(invocation) != null)
            {
                using (var _unitOfWork = BeginTransaction(invocation))
                {
                    int? hashCode = _unitOfWork?.GetHashCode();
                    invocation.Proceed();
                    try
                    {
                        await (Task)invocation.ReturnValue;
                        _unitOfWork?.Commit();
                    }
                    catch (Exception)
                    {
                        _unitOfWork?.Rollback();
                        throw;
                    }
                }
            }
            else
            {
                invocation.Proceed();
            }
        }

        /// <summary>
        /// 拦截返回结果为Task<TResult>的方法扩展
        /// </summary>
        /// <param name="invocation"></param>
        /// <typeparam name="TResult"></typeparam>
        public async Task<TResult> ExtensionInterceptAsynchronous<TResult>(IInvocation invocation)
        {
            TResult result;
            if (GetTransactionAttribute(invocation) != null)
            {
                using (var _unitOfWork = BeginTransaction(invocation))
                {
                    int? hashCode = _unitOfWork?.GetHashCode();
                    try
                    {
                        invocation.Proceed();
                        var returnValue = (Task<TResult>)invocation.ReturnValue;
                        result = await returnValue;

                        if (result is OperateResult)
                        {
                            var operatResult = result as OperateResult;
                            if (operatResult != null && operatResult.Success)
                                _unitOfWork?.Commit();
                            else
                                _unitOfWork?.Rollback();
                        }
                        else
                            _unitOfWork?.Commit();
                    }
                    catch (Exception)
                    {
                        _unitOfWork?.Rollback();
                        throw;
                    }
                }
            }
            else
            {
                invocation.Proceed();
                result = await (Task<TResult>)invocation.ReturnValue;
            }
            return result;
        }


        /// <summary>
        /// 开始事务
        /// </summary>
        /// <param name="invocation"></param>
        /// <returns></returns>
        private IUnitOfWork BeginTransaction(IInvocation invocation)
        {
            var transaction = GetTransactionAttribute(invocation);
            return _unitOfWorkManager.Begin(transaction.Propagation, transaction.IsolationLevel);
        }

        private TransactionAttribute GetTransactionAttribute(IInvocation invocation)
        {
            var method = invocation.MethodInvocationTarget ?? invocation.Method;
            return method.GetCustomAttribute<TransactionAttribute>(true);
        }

        #endregion
    }
}
